/**
 *  @file enumT.h
 */
#ifndef _ENUMT_H_
#define _ENUMT_H_

#include "../common/commontypes.h"
#include "../common/CppMemPool.h"
#include "../exceptions/ArgumentException.h"
#include "../text/CFString.h"
#include "Primitive.h"
#include "../collections/HashtableT.h"


namespace cppflib
{

namespace lang
{

namespace Enum
{

/*
 *  enumT.h and enumDecl.h work together to make "enum <-> string" conversion table easier 
 *
 *  Steps to make enums with conversion table:
 *
 *  1) First create a header file (.h), say "MyEnumDecl.h", and declare enum using macros in 
 *     "enumDecl.h" like this
 *
 *     #if (!defined(_MYENUMDECL_H_) || defined(GENERATE_ENUM_TABLE))
 *     
 *     #if (!defined(GENERATE_ENUM_TABLE))
 *     #   define _MYENUMDECL_H_
 *     #endif
 *     
 *     #if defined(_CUSTOM_ENUMT_DLLAPI)
 *     #   undef _CUSTOM_ENUMT_DLLAPI
 *     #endif
 *     #define _CUSTOM_ENUMT_DLLAPI _DLLAPI
 *     
 *     #include "lang/enumDecl.h"
 *     
 *     //! Declare DAYS in global namespace
 *     BEGIN_DECL_ENUM(DAYS)
 *        DV_ENUM(Sunday, 1)
 *        D_ENUM(Monday)
 *        D_ENUM(Tuesday)
 *        D_ENUM(Wednesday)
 *        D_ENUM(Thursday)
 *        D_ENUM(Friday)
 *     END_DECL_ENUM
 *     
 *     //! Declare MONTHS under namespace hello::world
 *     BEGIN_DECL_ENUM_NS2(hello, world, MONTHS)
 *        DV_ENUM(Jan, 1)
 *        D_ENUM(Feb)
 *        DV_ENUM(Mar, 10)
 *        D_ENUM(Apr)
 *        D_ENUM(May)
 *        D_ENUM(Jun)
 *     END_DECL_ENUM_NS2
 *     
 *     #endif
 *
 *     The first example declare enum "DAYS" in global namespace whereas second one declares "MONTHS" 
 *     under namespace "hello::world". 
 *     This header can then be included by any cpp file.
 *    
 *  2) Implement a cpp file, say "MyEnumDecl.cpp" to contain the actual code and conversion table
 *
 *     //! include once to get the normal enum declaration
 *     #include "MyEnumDecl.h"
 *
 *     #include "lang/enumT.h" // to include declaration of GetEnumTable
 *
 *     //! include again and define GENERATE_ENUM_TABLE to generate the tables
 *     #define GENERATE_ENUM_TABLE
 *     #include "MyEnumDecl.h"
 *     #undef GENERATE_ENUM_TABLE
 *
 *  3) Include "MyEnumDecl.cpp" in the compiling process and use the template functions declared in this
 *     header to do enum <-> string conversion and/or test existence of a enum value, e.g.
 *
 *     Enum::IsDefined<DAYS>(Sunday);
 *     Enum::Parse<DAYS>(_S("Friday"));
 *     Enum::GetString<DAYS>(Monday);
 *     Enum::GetTotalEnums<DAYS>();
 *
 */


/**
 *   Function to get a particular enum table.
 *   It is not intended to be used directly by normal user.
 */
template <typename ENUMTYPE>
const collections::HashtableT<CFString, Int32> * GetEnumTable(void);


/**
 *   Check whether a enum exists
 */
template <typename ENUMTYPE>
bool IsDefined(ENUMTYPE enumVal)
{  
   u64_t pos = 0;
   const CFString *pKey = NULL;
   Int32 *pVal = NULL;
   const collections::HashtableT<CFString, Int32> * pTbl = GetEnumTable<ENUMTYPE>();

   while (pTbl->EnumerateElement(pos, &pKey, &pVal)) {
      if (pVal->Value() == static_cast<int>(enumVal))
         return true;
   }

   return false;
}

/**
 *   Convert string to enum value
 */
template <typename ENUMTYPE>
ENUMTYPE Parse(pcwstr_t szString)
{
   CFString key(szString);
   Int32 *pVal = NULL;
   const collections::HashtableT<CFString, Int32> * pTbl = GetEnumTable<ENUMTYPE>();

   pVal = pTbl->Get(key);
   if (pVal) {
      return static_cast<ENUMTYPE>(pVal->Value());
   }

   throw exceptions::ArgumentException(_S("Can't find matched enum string"), -1);
   return static_cast<ENUMTYPE>(0);
}


/**
 *   Convert enum to string
 */
template <typename ENUMTYPE>
pcwstr_t GetString(ENUMTYPE enumVal)
{
   u64_t pos = 0;
   const CFString *pKey = NULL;
   Int32 *pVal = NULL;
   const collections::HashtableT<CFString, Int32> * pTbl = GetEnumTable<ENUMTYPE>();

   while (pTbl->EnumerateElement(pos, &pKey, &pVal)) {
      if (pVal->Value() == static_cast<int>(enumVal))
         return static_cast<pcwstr_t>(*pKey);
   }

   throw exceptions::ArgumentException(_S("Can't find matched enum value"), -1);
   return NULL;
}

/**
 *   Get total number of enum of a particular type
 */
template <typename ENUMTYPE>
sz_t GetTotalEnums(void)
{
   const collections::HashtableT<CFString, Int32> * pTbl = GetEnumTable<ENUMTYPE>();
   return pTbl->GetCount();
}


}  // end of namespace Enum

}  // end of namespace lang

}  // end of namespace cppflib


#endif
